/**
 * 
 */
package javavis.jip2d.functions.tia2012;

import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.concurrent.*;
import javavis.base.JIPException;
import javavis.jip2d.base.bitmaps.JIPBmpFloat;

/**
 * @author asc
 *
 */
public class Feature extends Thread implements Serializable {

    /**
     * 
     */

    protected double error;
    protected float umbral;
    protected int paridad;
    protected float[] listaumbrales;
    protected double[] listaPesos;
    protected ArrayList<Region> listaRegiones;
    /*
    protected ArrayList<TrainExample> listaCaras;
    protected ArrayList<TrainExample> listaNoCaras;
     */
    
    protected TrainExample[] listaCaras;
    protected TrainExample[] listaNoCaras;



    /**
     * @param posX
     * @param posY
     * @param Width
     * @param Height
     * @throws JIPException
     */
    public Feature(int posX, int posY, int Width, int Height)throws JIPException  {
        // TODO Auto-generated constructor stub

        if (posX+Width>24 || posY+Height>24 || Width>24 || Height>24) {
            throw new JIPException("La Feature se sale de la imagen");
        } 
        listaRegiones = new ArrayList<Region>();

        paridad=1;
        umbral = 0;
        error = Double.MAX_VALUE;
    }

    /**
     * @param integral
     * @return
     * @throws JIPException
     */
    public float getValor(JIPBmpFloat integral) throws JIPException{
        float valor=0;
        boolean resta=false;
        for (Region reg : listaRegiones) {
            if(resta){
                valor-=reg.getValor(integral);
            }
            else{
                valor+=reg.getValor(integral);
            }
            resta=!resta;
        }
        return valor;
    }
    
    public float getValor(JIPBmpFloat integral, int ancho, int alto) throws JIPException{
        float valor=0;
        boolean resta=false;
        for (Region reg : listaRegiones) {
            if(resta){
                valor-= reg.getValor(integral,ancho,alto);
            }
            else{
                valor+= reg.getValor(integral,ancho,alto);
            }
            resta=!resta;
        }
        return valor;
    }

    /**
     * @return the listaRegiones
     */
    public ArrayList<Region> getListaRegiones()
    {
        return listaRegiones;
    }

    /**
     * @param listaRegiones the listaRegiones to set
     */
    public void setListaRegiones(ArrayList<Region> listaRegiones)
    {
        this.listaRegiones = listaRegiones;
    }

    /**
     * @return the umbral
     */
    public float getUmbral()
    {
        return umbral;
    }

    /**
     * @param umbral the umbral to set
     */
    public void setUmbral(float umbral)
    {
        this.umbral = umbral;
    }

    /**
     * @return the paridad
     */
    public int getParidad()
    {
        return paridad;
    }

    /**
     * @param paridad the paridad to set
     */
    public void setParidad(int paridad)
    {
        this.paridad = paridad;
    }


    /**
     * @return the error
     */
    public double getError()
    {
        return error;
    }

    /**
     * @param error the error to set
     */
    public void setError(double error)
    {
        this.error = error;
    }

    public void recalculaPesos(TrainExample[] listaCaras, TrainExample[] listaNoCaras)
    {
        double errortmp = error;
        for (TrainExample trainExample : listaCaras)
        {
            try
            {
                if(getValor(trainExample.getIntegral()) < umbral)
                {
                    trainExample.actualiza(errortmp);
                }
            }
            catch (JIPException e)
            {

                e.printStackTrace();
            }
        }

        for (TrainExample trainExample : listaNoCaras)
        {
            try
            {
                if(getValor(trainExample.getIntegral()) > umbral)
                {
                    trainExample.actualiza(errortmp);
                }
            }
            catch (JIPException e)
            {

                e.printStackTrace();
            }
        }
    }

    //public void entrenarFeature(ArrayList<TrainExample> listaCaras, ArrayList<TrainExample> listaNoCaras)
    public void entrenarFeature()
    {
        /*
         * Buscar Umbrales
         * 
         */
        int Caras=listaCaras.length;
        int noCaras= listaNoCaras.length;
        int tam = Caras + noCaras;
        //HashMap<Float,Float> hashUmbrales = new HashMap<Float,Float>(tam);
        listaumbrales = new float[tam];
        listaPesos = new double[tam];
        //float[] umbrales = new float[tam];
        int i=0;
        try
        {
            for (int k=0; k<Caras;k++) //TrainExample trainExample : listaCaras)
            {
                    listaumbrales[i]=getValor(listaCaras[k].getIntegral());
                    listaPesos[i]=listaCaras[k].getPeso();
                    /*
                    if(!hashUmbrales.containsKey(listaumbrales[i]))
                    {
                        hashUmbrales.put(listaumbrales[i]+1,listaumbrales[i]+1);
                        umbrales[i]=listaumbrales[i];
                    }
                    else
                    {
                        umbrales[i]=Float.MIN_VALUE;
                    }
                    */
                    i++;
            }
            for (int k=0; k<noCaras;k++)//(TrainExample trainExample : listaNoCaras)
            {
                    listaumbrales[i]=getValor(listaNoCaras[k].getIntegral());
                    listaPesos[i]=listaNoCaras[k].getPeso();
                    /*
                    if(!hashUmbrales.containsKey(listaumbrales[i]))
                    {
                        hashUmbrales.put(listaumbrales[i]+1,listaumbrales[i]+1);
                        umbrales[i]=listaumbrales[i];
                    }
                    else
                    {
                        umbrales[i]=Float.MIN_VALUE;
                    }
                    */
                    i++;
            }
        }
        catch (JIPException e)
        {

            e.printStackTrace();
        }

        /*
         * Encontrar el valor del umbral que haga
         * que la suma de los pesos de los mal clasificados sea minima
         * 
         * mejor umbral,paridad
         * error
         * retorna error
         */

        
        Double errorTemp0=0.0;
        Double errorTemp1=0.0;
        
        //hashUmbrales=null;
        //error = AdaBoost.globalError +1;
        
        float testUmbral=0;
        for (int k=0; k<tam;k++)//(float testUmbral : listaumbrales)
        {
            testUmbral=listaumbrales[k];
            //if(testUmbral == Float.MIN_VALUE) continue;
            
                    //if(error > AdaBoost.globalError+1)error = AdaBoost.globalError+1;
                    errorTemp0=0.0;
                    errorTemp1=0.0;
        
                    for(i=0; i<Caras && (error > errorTemp0 || error > errorTemp1); i++)
                    {
                        // 1*getValor(imag.getIntegral()) < 1*umbralTest   => esCara 1
                        // 1*getValor(imag.getIntegral()) > 1*umbralTest   => Resto 0
        
                        if(error > errorTemp0  && listaumbrales[i] >= testUmbral)
                        {
                            //SUMA (PESO * ABS(Resto-Cara))
                            errorTemp0+=listaPesos[i];
                        }
        
                        // -1*getValor(imag.getIntegral()) < -1*umbralTest   => esCara 1
                        // -1*getValor(imag.getIntegral()) > -1*umbralTest   => Resto 0
                        if(error > errorTemp1 && -listaumbrales[i] >= -testUmbral)
                        {
                            //SUMA (PESO * ABS(Resto-Cara))
                            errorTemp1+=listaPesos[i];
                        }
        
                    }
        
                    for(i=listaCaras.length;i<tam && (error > errorTemp0 || error > errorTemp1);i++)
                    {
        
                        // 1*getValor(imag.getIntegral()) < 1*umbralTest   => esCara 1
                        // 1*getValor(imag.getIntegral()) > 1*umbralTest   => Resto 0
                        if(error > errorTemp0 && listaumbrales[i] < testUmbral)
                        {
                            //SUMA (PESO * ABS(esCara-noCara))
                            errorTemp0+=listaPesos[i];
                        }
        
                        // -1*getValor(imag.getIntegral()) < -1*umbralTest   => esCara 1
                        // -1*getValor(imag.getIntegral()) > -1*umbralTest   => Resto 0
                        if(error > errorTemp1 && -listaumbrales[i] < -testUmbral)
                        {
                            //SUMA (PESO * ABS(esCara-noCara))
                            errorTemp1+=listaPesos[i];
                        }
        
                    }
        
        
                    if(error > errorTemp0) 
                    {
                        error = errorTemp0;
                        umbral=testUmbral;
                        paridad = 1;
                        /*
                        if(error < AdaBoost.globalError)
                        {
                            AdaBoost.globalError=error;
                        }
                        */
                    }
                    if(error > errorTemp1) 
                    {
                        error = errorTemp1;
                        umbral=testUmbral;
                        paridad = -1;
                        /*
                        if(error < AdaBoost.globalError)
                        {
                            AdaBoost.globalError=error;
                        }
                        */
                    }
                }
            
        listaumbrales=null;
        listaPesos=null;
        
    }

    /**
     * @return the listaCaras
     */
    public TrainExample[] getListaCaras()
    {
        return listaCaras;
    }

    /**
     * @param listaCaras the listaCaras to set
     */
    public void setListaCaras(TrainExample[] listaCaras)
    {
        this.listaCaras = listaCaras;
    }

    /**
     * @return the listaNoCaras
     */
    public TrainExample[] getListaNoCaras()
    {
        return listaNoCaras;
    }

    /**
     * @param listaNoCaras the listaNoCaras to set
     */
    public void setListaNoCaras(TrainExample[] listaNoCaras)
    {
        this.listaNoCaras = listaNoCaras;
    }

    /* (non-Javadoc)
     * @see java.lang.Thread#run()
     */
    @Override
    public void run()
    {
        entrenarFeature();
    }
    
    

}
